from tkinter import * 
from PIL import Image, ImageTk
from math import cos, sin, pi
import numpy as np
import math

class Application(Frame) : 
    def __init__(self , master = None) : 
        Frame.__init__(self , master = None)
        self.pack()
        self.createWidgets()
    
    ###############################################################
    # 上传图片接口 具体过程为 ： 读取图片路径 -> 获得图片 -> 展示图片
    def uploadPicture(self) : 
        self.imagePath = self.entry.get()
        # 异常处理 ， 用户输入路径不正确则要提示用户，直到用户输入正确为止，不要让人觉得云里雾里
        try : 
            self.image = Image.open(self.imagePath)
            
            input_width = self.canvasChild.winfo_width()
            input_height = self.canvasChild.winfo_width()

            self.image = self.image.resize((input_width , input_height) , Image.ANTIALIAS)
            photo = ImageTk.PhotoImage(self.image)
            self.canvasChild.create_image(0, 0, anchor=NW, image=photo)
            self.canvasChild.image = photo
        except FileNotFoundError : 
            self.uploadLable = Label(self , text = "上传路径不正确，请检查:)~")
            self.uploadLable.config(foreground="red")
            self.canvas.create_window(300 , 80 , window=self.uploadLable)
            self.after(1000 , lambda: self.uploadLable.destroy())
        return

    # 下载图像接口
    def downloadPicture(self) :
        # 异常处理 用户没有上传图片则要下载，此时要提醒用户上传图片
        try: 
            self.image.save("result.jpg")
            self.downloadLable = Label(self , text = "恭喜您下载图片成功，图片存放在同级目录下~")
            self.downloadLable.config(foreground="green")
            self.canvas.create_window(1250 , 80 , window=self.downloadLable)
        except AttributeError :
            self.downloadLable = Label(self , text = "您还没有上传图片QAQ~")
            self.downloadLable.config(foreground="red")
            self.canvas.create_window(1200 , 80 , window=self.downloadLable)
        # 1秒后销毁提示信息
        # lambda表达式是定义了一个简单的函数，这种函数我们只需要使用一次，单独定义会让整个项目难管理，所以采用匿名函数形式
        self.after(1000 , lambda: self.downloadLable.destroy())
    
    ###############################################################
    # 创建第一级按钮的位置(回退后调用)
    def btn_1_create(self) : 
        self.btn1 = Button(self , text = "几何变换" , width=10 , height=2 , command=self.btn1_click)
        self.canvas.create_window(850 , 140 , window=self.btn1)
        self.btn2 = Button(self , text = "像素变换" , width=10 , height=2 , command=self.btn2_click)
        self.canvas.create_window(850 , 220 , window=self.btn2)
        self.btn3 = Button(self , text = "图像去噪" , width=10 , height=2 , command=self.btn3_click)
        self.canvas.create_window(850 , 300 , window=self.btn3)
        self.btn4 = Button(self , text = "图像锐化" , width=10 , height=2 , command=self.btn4_click)
        self.canvas.create_window(850 , 380 , window=self.btn4)
        self.btn5 = Button(self , text = "边缘检测" , width=10 , height=2 , command=self.btn5_click)
        self.canvas.create_window(850 , 460 , window=self.btn5)
        self.btn6 = Button(self , text = "图像分割" , width=10 , height=2 , command=self.btn6_click)
        self.canvas.create_window(850 , 540 , window=self.btn6)
        self.btn7 = Button(self , text = "图像分类" , width=10 , height=2 , command=self.btn7_click)
        self.canvas.create_window(850 , 620 , window=self.btn7)

    # 删除第一级原位置的按钮(进入子目录时调用)
    def btn_1_delete(self) : 
        self.btn1.destroy()
        self.btn2.destroy()
        self.btn3.destroy()
        self.btn4.destroy()
        self.btn5.destroy()
        self.btn6.destroy()
        self.btn7.destroy()
    

    ############################################################################################
    # 第一任务方法回退到上一级目录(销毁按钮)
    def btn1_back(self) : 
        self.btn_back.destroy()
        self.btn1_1.destroy()
        self.btn1_2.destroy()
        self.btn1_3.destroy()
        self.btn1_4.destroy()
        self.btn1_5.destroy()
        self.btn1_6.destroy()
        self.btn_1_create()

    # 第一任务方法 -- 几何变换
    def btn1_click(self) : 
        self.btn_1_delete()
        self.btn_back = Button(self , text="<- 回退到上一级" , width=20 , height=2 , command=self.btn1_back)
        self.canvas.create_window(850 , 40 , window=self.btn_back)

        self.btn1_1 = Button(self , text = "缩放" , width=10 , height=2 , command=self.btn1_1_click)
        self.canvas.create_window(850 , 160 , window=self.btn1_1)
        self.btn1_2 = Button(self , text = "平移" , width=10 , height=2 , command=self.btn1_2_click)
        self.canvas.create_window(850 , 240 , window=self.btn1_2)
        self.btn1_3 = Button(self , text = "旋转" , width=10 , height=2 , command=self.btn1_3_click)
        self.canvas.create_window(850 , 320 , window=self.btn1_3)
        self.btn1_4 = Button(self , text = "翻转" , width=10 , height=2 , command=self.btn1_4_click)
        self.canvas.create_window(850 , 400 , window=self.btn1_4)
        self.btn1_5 = Button(self , text = "剪切" , width=10 , height=2 , command=self.btn1_5_click)
        self.canvas.create_window(850 , 480 , window=self.btn1_5)
        self.btn1_6 = Button(self , text = "自由旋转" , width=10 , height=2)
        self.canvas.create_window(850 , 560 , window=self.btn1_6)
        print(1)

    #第一任务几何变换的缩放（默认缩放比例为0.8）
    def btn1_1_click(self):
        #默认缩放比例为0.8
        scale_ratio = 0.8
        # 获取图像的原始宽度和高度
        width, height = self.image.size
        # 计算缩放后的图像宽度和高度
        new_width = (int)(width * scale_ratio)
        new_height = (int)(height * scale_ratio)
        # 创建新的空白图像，模式与原图像一致
        new_image = Image.new(self.image.mode, (width, height))
        # 遍历新图像的每个像素点
        for new_y in range(new_height):
            for new_x in range(new_width):
                # 根据缩放比例计算对应原图像中的坐标
                old_x = int(new_x / scale_ratio)
                old_y = int(new_y / scale_ratio)
                # 获取原图像中对应坐标的像素值
                pixel = self.image.getpixel((old_x, old_y))
                # 将像素值设置到新图像对应的坐标位置
                new_image.putpixel((new_x, new_y), pixel)
        self.image = new_image
        #把变换后的图像显示到窗口
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo    

    #第一任务几何变换的平移（默认向右100，向下50）
    def btn1_2_click(self):
        offset_x = 100
        offset_y = 50
        # 获取图像的宽度和高度
        width, height = self.image.size
        # 创建新的空白图像，尺寸与原图像相同，模式也相同
        new_image = Image.new(self.image.mode, (width, height))
        # 遍历原图像的每个像素点
        for y in range(height):
            for x in range(width):
                # 计算平移后在新图像中的坐标位置
                new_x = x + offset_x
                new_y = y + offset_y
                # 判断新坐标是否在新图像范围内，如果不在则不设置像素即默认黑色
                if 0 <= new_x < width and 0 <= new_y < height:
                    # 获取原图像中当前像素点的像素值
                    pixel = self.image.getpixel((x, y))
                    # 将像素值设置到新图像对应的坐标位置
                    new_image.putpixel((new_x, new_y), pixel)
        self.image = new_image
        # 把变换后的图像显示到窗口
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo        

    #第一任务几何变换的旋转（默认九十度）
    def btn1_3_click(self):
        #默认旋转角度为90度
        angle = 90
        # 获取图像的宽度和高度
        width, height = self.image.size
        # 计算图像中心点坐标
        center_x, center_y = width / 2, height / 2
        # 将角度转换为弧度
        radian_angle = angle * pi / 180
        # 计算旋转后的图像的尺寸（取整数），以确保能容纳旋转后的整个图像
        new_width = int(abs(width * cos(radian_angle)) + abs(height * sin(radian_angle)))
        new_height = int(abs(width * sin(radian_angle)) + abs(height * cos(radian_angle)))
        # 创建新的空白图像，尺寸为计算出的新尺寸，模式与原图像相同
        new_image = Image.new(self.image.mode, (new_width, new_height))
        # 遍历新图像的每个像素点
        for y in range(new_height):
            for x in range(new_width):
                # 将新图像坐标转换到以原图像中心点为原点的坐标系下
                dx = x - new_width / 2
                dy = y - new_height / 2
                # 根据旋转公式进行坐标变换，绕中心点旋转
                rotated_dx = dx * cos(radian_angle) - dy * sin(radian_angle)
                rotated_dy = dx * sin(radian_angle) + dy * cos(radian_angle)
                # 再将坐标转换回原图像的坐标系
                old_x = int(rotated_dx + center_x)
                old_y = int(rotated_dy + center_y)
                # 判断坐标是否在原图像范围内
                if 0 <= old_x < width and 0 <= old_y < height:
                    # 获取原图像中对应坐标的像素值
                    pixel = self.image.getpixel((old_x, old_y))
                    # 将像素值设置到新图像对应的坐标位置
                    new_image.putpixel((x, y), pixel)
        self.image = new_image
        # 把变换后的图像显示到窗口
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo                

    #第一任务几何变换的翻转
    def btn1_4_click(self):
        width, height = self.image.size
        # 创建新的空白图像，尺寸与原图像相同，模式也相同
        new_image = Image.new(self.image.mode, (width, height))
        # 遍历原图像的每个像素点
        for y in range(height):
            for x in range(width):
            # 计算水平翻转后在新图像中的对应坐标
                new_x = width - 1 - x
                new_y = y
                # 获取原图像中当前像素点的像素值
                pixel = self.image.getpixel((x, y))
                # 将像素值设置到新图像对应的坐标位置
                new_image.putpixel((new_x, new_y), pixel)
        self.image = new_image
        # 把变换后的图像显示到窗口
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo        

    #第一任务几何变换的剪切
    #默认剪切的图片左边界的横坐标为100，上边界的纵坐标为100，右边界的横坐标为500，下边界的纵坐标为500
    def btn1_5_click(self):
        left = 100
        right = 500
        top = 100
        bottom = 500
        # 获取图像的宽度和高度
        width, height = self.image.size
        # 检查剪切区域参数是否合法，确保在图像范围内
        if left < 0 or top < 0 or right > width or bottom > height or left >= right or top >= bottom:
            #手动引发异常
            raise ValueError("剪切区域参数不合法")
        # 计算剪切后图像的宽度和高度
        cut_width = right - left
        cut_height = bottom - top
        # 创建新的空白图像，尺寸为剪切区域大小，模式与原图像相同
        new_image = Image.new(self.image.mode, (cut_width, cut_height))
        # 遍历剪切区域内的每个像素点
        for y in range(cut_height):
            for x in range(cut_width):
                # 计算在原图像中的对应坐标
                old_x = x + left
                old_y = y + top
                # 获取原图像中对应坐标的像素值
                pixel = self.image.getpixel((old_x, old_y))
                #将像素值设置到新图像对应的坐标位置
                new_image.putpixel((x, y), pixel)
        self.image = new_image
        # 把变换后的图像显示到窗口
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo        
        
    ###############################################################
    # 第二任务方法回退到上一级目录(销毁按钮)
    def btn2_back(self) : 
        self.btn_back.destroy()
        self.btn2_1.destroy()
        self.btn2_2.destroy()
        self.btn2_3.destroy()
        self.btn2_4.destroy()
        self.btn_1_create()

     ###################################################################################################################################### 
    # 第三级目录回到上一级
    def btn2_back_back(self) : 
        self.btn3_1.destroy()
        self.btn3_2.destroy()
        self.btn3_3.destroy()
        self.btn3_4.destroy()
        self.btn3_5.destroy()
        self.btn3_6.destroy()
        self.btn_back.destroy()

        self.btn_back = Button(self , text="<- 回退到上一级" , width=20 , height=2 , command=self.btn2_back)
        self.canvas.create_window(850 , 40 , window=self.btn_back)
        self.btn2_1 = Button(self , text="图像间运算" , width=10 , height=2 , command=self.btn2_1_click)
        self.canvas.create_window(850 , 200 , window=self.btn2_1)
        self.btn2_2 = Button(self , text = "灰度变换" , width=10 , height=2)
        self.canvas.create_window(850 , 300 , window=self.btn2_2)
        self.btn2_3 = Button(self , text = "直方图计算" , width=10 , height=2)
        self.canvas.create_window(850 , 400 , window=self.btn2_3)
        self.btn2_4 = Button(self , text = "直方图均衡" , width=10 , height=2)
        self.canvas.create_window(850 , 500 , window=self.btn2_4)
        
        self.canvasChild.destroy()
        self.canvasChild_copy.destroy()
        self.btnUpload_copy.destroy()
        self.entry_copy.destroy()

        self.canvasChild = Canvas(self , width = 600 , height=600 , bg="gray")
        self.canvas.create_window(400 , 400 , window=self.canvasChild)
        
   
    # 第二个输入框的上传逻辑
    def uploadPicture_copy(self) : 
        self.imagePath_copy = self.entry_copy.get()
        # 异常处理 ， 用户输入路径不正确则要提示用户，直到用户输入正确为止，不要让人觉得云里雾里
        try : 
            self.image_copy = Image.open(self.imagePath_copy)
            
            input_width = self.canvasChild.winfo_width()
            input_height = self.canvasChild.winfo_width()

            self.image_copy = self.image_copy.resize((input_width , input_height) , Image.ANTIALIAS)
            photo = ImageTk.PhotoImage(self.image_copy)
            self.canvasChild_copy.create_image(0, 0, anchor=NW, image=photo)
            self.canvasChild_copy.image = photo
        except FileNotFoundError : 
            self.uploadLable_copy = Label(self , text = "上传路径不正确，请检查:)~")
            self.uploadLable_copy.config(foreground="red")
            self.canvas.create_window(300 , 440 , window=self.uploadLable_copy)
            self.after(1000 , lambda: self.uploadLable_copy.destroy())
        return

    # 图像间加运算
    def btn2_1_1_click(self) : 
        image01 = self.image.convert('RGB')
        image02 = self.image_copy.convert('RGB')

        img1_array = np.array(image01)
        img2_array = np.array(image02)

        width , height = self.image.size
        new_image = np.zeros((width , height , 3))
        print(new_image)

        for y in range(height) : 
            for x in range(width) : 
                for k in range(3) : 
                    new_image[y][x][k] = img1_array[y][x][k] + img2_array[y][x][k]
                    if(new_image[y][x][k] > 255) : 
                        new_image[y][x][k] = 255

        # result_array = img1_array + img2_array
        # result_array = np.clip(result_array , 0 , 255)
        # result_img = Image.fromarray(result_array.astype('uint8'))

        result_img = Image.fromarray(new_image.astype('uint8'))
        
        # 使用winfo_width时要记得加括号 ， 否则返回的就只是函数地址 ， 我们希望返回的是形状
        result_width = self.canvasRightChild.winfo_width()
        result_height = self.canvasRightChild.winfo_height()

        result_img = result_img.resize((result_width , result_height) , Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(result_img)

        self.canvasRightChild.create_image(0 , 0 , anchor=NW , image = photo)
        self.canvasRightChild.image = photo

    # 图像间减运算
    def btn2_1_2_click(self) : 
        image01 = self.image.convert('RGB')
        image02 = self.image_copy.convert('RGB')
        img1_array = np.array(image01)
        img2_array = np.array(image02)

        width , height = self.image.size
        new_image = np.zeros((width , height , 3))
        print(new_image)

        for y in range(height) : 
            for x in range(width) : 
                for k in range(3) : 
                    new_image[y][x][k] = img1_array[y][x][k] - img2_array[y][x][k]
                    if(new_image[y][x][k] < 0) : 
                        new_image[y][x][k] = 0

        result_img = Image.fromarray(new_image.astype('uint8'))
        
        # 使用winfo_width时要记得加括号 ， 否则返回的就只是函数地址 ， 我们希望返回的是形状
        result_width = self.canvasRightChild.winfo_width()
        result_height = self.canvasRightChild.winfo_height()

        result_img = result_img.resize((result_width , result_height) , Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(result_img)

        self.canvasRightChild.create_image(0 , 0 , anchor=NW , image = photo)
        self.canvasRightChild.image = photo

    # 图像间与运算
    def btn2_1_3_click(self) : 
        image01 = self.image.convert('RGB')
        image02 = self.image_copy.convert('RGB')
        img1_array = np.array(image01)
        img2_array = np.array(image02)

        width , height = self.image.size
        new_image = np.zeros((width , height , 3))
        print(new_image)

        for y in range(height) : 
            for x in range(width) : 
                for k in range(3) : 
                    new_image[y][x][k] = img1_array[y][x][k] & img2_array[y][x][k]
                    if(new_image[y][x][k] < 0) : 
                        new_image[y][x][k] = 0

        result_img = Image.fromarray(new_image.astype('uint8'))
        
        # 使用winfo_width时要记得加括号 ， 否则返回的就只是函数地址 ， 我们希望返回的是形状
        result_width = self.canvasRightChild.winfo_width()
        result_height = self.canvasRightChild.winfo_height()

        result_img = result_img.resize((result_width , result_height) , Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(result_img)

        self.canvasRightChild.create_image(0 , 0 , anchor=NW , image = photo)
        self.canvasRightChild.image = photo
    
    # 图像间或运算
    def btn2_1_4_click(self) : 
        image01 = self.image.convert('RGB')
        image02 = self.image_copy.convert('RGB')
        img1_array = np.array(image01)
        img2_array = np.array(image02)

        width , height = self.image.size
        new_image = np.zeros((width , height , 3))
        print(new_image)

        for y in range(height) : 
            for x in range(width) : 
                for k in range(3) : 
                    new_image[y][x][k] = img1_array[y][x][k] | img2_array[y][x][k]
                    if(new_image[y][x][k] < 0) : 
                        new_image[y][x][k] = 0

        result_img = Image.fromarray(new_image.astype('uint8'))
        
        # 使用winfo_width时要记得加括号 ， 否则返回的就只是函数地址 ， 我们希望返回的是形状
        result_width = self.canvasRightChild.winfo_width()
        result_height = self.canvasRightChild.winfo_height()

        result_img = result_img.resize((result_width , result_height) , Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(result_img)

        self.canvasRightChild.create_image(0 , 0 , anchor=NW , image = photo)
        self.canvasRightChild.image = photo
    
    # 图像间补运算
    def btn2_1_5_click(self) : 
        return
    
    # 图像间异或运算
    def btn2_1_6_click(self) : 
        return
        

    def btn2_1_click(self) : 
        self.btn_back.destroy()
        self.btn2_1.destroy()
        self.btn2_2.destroy()
        self.btn2_3.destroy()
        self.btn2_4.destroy()

        self.btn_back = Button(self , text="<- 回退到上一级" , width=20 , height=2 , command=self.btn2_back_back)
        self.canvas.create_window(850 , 40 , window=self.btn_back)

        # 创建第三级目录
        self.btn3_1 = Button(self , text = "图像加" , width=10 , height=2 , command=self.btn2_1_1_click)
        self.canvas.create_window(850 , 180 , window=self.btn3_1)
        self.btn3_2 = Button(self , text = "图像减" , width=10 , height=2 , command=self.btn2_1_2_click)
        self.canvas.create_window(850 , 260 , window=self.btn3_2)
        self.btn3_3 = Button(self , text = "图像与" , width=10 , height=2 , command=self.btn2_1_3_click)
        self.canvas.create_window(850 , 340 , window=self.btn3_3)
        self.btn3_4 = Button(self , text = "图像或" , width=10 , height=2 , command=self.btn2_1_4_click)
        self.canvas.create_window(850 , 420 , window=self.btn3_4)
        self.btn3_5 = Button(self , text = "图像补" , width=10 , height=2 , command=self.btn2_1_5_click)
        self.canvas.create_window(850 , 500 , window=self.btn3_5)  
        self.btn3_6 = Button(self , text = "图像异或" , width=10 , height=2 , command=self.btn2_1_6_click)
        self.canvas.create_window(850 , 580 , window=self.btn3_6)

        # 移动第一个展示框的位置 (销毁后创造，因为移动会提高耦合性)
        self.canvasChild.destroy()
        self.canvasChild = Canvas(self , width = 300 , height=300 , bg="gray")
        self.canvas.create_window(400 , 250 , window=self.canvasChild)
        # 创建第二个展示框 (同时要创建上传按钮和上传输入框)
        self.canvasChild_copy = Canvas(self , width = 300 , height=300 , bg="gray")
        self.canvas.create_window(400 , 600 , window=self.canvasChild_copy)

        self.btnUpload_copy = Button(self , text = "上传图片" , width=10 , height=2 , command=self.uploadPicture_copy)
        self.canvas.create_window(150 , 420 , window=self.btnUpload_copy)

        self.entry_copy = Entry(self , fg="gray")
        self.entry_copy.insert(0 , "请输入图片的完整路径~")
        self.canvas.create_window(300 , 420 , window=self.entry_copy)

    ######################################################################################################################################      

    # 第二任务方法 -- 像素变换
    def btn2_click(self) : 
        self.btn_1_delete()
        self.btn_back = Button(self , text="<- 回退到上一级" , width=20 , height=2 , command=self.btn2_back)
        self.canvas.create_window(850 , 40 , window=self.btn_back)
        self.btn2_1 = Button(self , text="图像间运算" , width=10 , height=2 , command=self.btn2_1_click)
        self.canvas.create_window(850 , 200 , window=self.btn2_1)
        self.btn2_2 = Button(self , text = "灰度变换" , width=10 , height=2)
        self.canvas.create_window(850 , 300 , window=self.btn2_2)
        self.btn2_3 = Button(self , text = "直方图计算" , width=10 , height=2)
        self.canvas.create_window(850 , 400 , window=self.btn2_3)
        self.btn2_4 = Button(self , text = "直方图均衡" , width=10 , height=2)
        self.canvas.create_window(850 , 500 , window=self.btn2_4)







    ############################################################################################
    # 第三任务方法回退到上一级目录(销毁按钮)
    def btn3_back(self) : 
        self.btn_back.destroy()
        self.btn3_1.destroy()
        self.btn3_2.destroy()
        self.btn3_3.destroy()
        self.btn3_4.destroy()
        self.btn_1_create()

    # 第三任务方法 -- 图像去噪
    def btn3_click(self) : 
        self.btn_1_delete()
        self.btn_back = Button(self , text="<- 回退到上一级" , width=20 , height=2 , command=self.btn3_back)
        self.canvas.create_window(850 , 40 , window=self.btn_back)
        
        self.btn3_1 = Button(self , text = "中值滤波" , width=10 , height=2 , command=self.btn3_1_click)
        self.canvas.create_window(850 , 100 , window=self.btn3_1)
        self.btn3_2 = Button(self , text = "均值滤波" , width=10 , height=2 , command=self.btn3_2_click)
        self.canvas.create_window(850 , 200 , window=self.btn3_2)
        self.btn3_3 = Button(self , text = "组合空域滤波" , width=10 , height=2 , command=self.btn3_3_click)
        self.canvas.create_window(850 , 300 , window=self.btn3_3)
        self.btn3_4 = Button(self , text = "频率滤波" , width=10 , height=2 , command=self.btn3_4_click)
        self.canvas.create_window(850 , 400 , window=self.btn3_4)
        return


    #中值滤波（默认滤波核大小为3）
    def btn3_1_click(self):
        #默认滤波核大小为3
        kernel_size = 3
        # 获取图像的宽度和高度
        width, height = self.image.size
        # 计算滤波核半径（用于后续遍历像素点周边范围）
        radius = kernel_size // 2
        # 创建新的空白图像，尺寸与原图像相同，模式也相同
        new_image = Image.new(self.image.mode, (width, height))
        # 遍历图像的每个像素点（避开边缘部分）
        for y in range(radius, height - radius):
            for x in range(radius, width - radius):
                # 分别用于存储红、绿、蓝通道滤波核覆盖像素点的像素值列表
                red_pixel_values = []
                green_pixel_values = []
                blue_pixel_values = []
                # 遍历滤波核范围内的像素点
                for ky in range(y - radius, radius + y):
                    for kx in range(x - radius, radius + x):
                        # 获取原图像中对应坐标的像素
                        pixel = self.image.getpixel((kx, ky))
                        red, green, blue = pixel
                        red_pixel_values.append(red)
                        green_pixel_values.append(green)
                        blue_pixel_values.append(blue)
                # 对各通道的像素值列表进行排序
                sorted_red_pixel_values = sorted(red_pixel_values)
                sorted_green_pixel_values = sorted(green_pixel_values)
                sorted_blue_pixel_values = sorted(blue_pixel_values)
                # 取各通道的中值
                median_red = sorted_red_pixel_values[len(sorted_red_pixel_values) // 2]
                median_green = sorted_green_pixel_values[len(sorted_green_pixel_values) // 2]
                median_blue = sorted_blue_pixel_values[len(sorted_blue_pixel_values) // 2]
                # 将中值设置到新图像对应的坐标位置
                new_image.putpixel((x, y), (median_red, median_green, median_blue))
        #将新图像写回原图像
        self.image = new_image
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo        

    #均值滤波（默认滤波核大小为3）
    def btn3_2_click(self):
        #默认滤波核大小为3
        kernel_size = 3
        # 获取图像的宽度和高度
        width, height = self.image.size
        # 计算滤波核半径（用于后续遍历像素点周边范围）
        radius = kernel_size // 2
        # 创建新的空白图像，尺寸与原图像相同，模式也相同
        new_image = Image.new(self.image.mode, (width, height))
        # 遍历图像的每个像素点（避开边缘部分）
        for y in range(radius, height - radius):
            for x in range(radius, width - radius):
                # 分别用于存储红、绿、蓝通道滤波核覆盖像素点的像素值总和
                sum_red = 0
                sum_green = 0
                sum_blue = 0
                # 遍历滤波核范围内的像素点
                for ky in range(y - radius, radius + y):
                    for kx in range(x - radius, radius + x):
                        # 获取原图像中对应坐标的像素
                        pixel = self.image.getpixel((kx, ky))
                        red, green, blue = pixel
                        sum_red += red
                        sum_green += green
                        sum_blue += blue
                # 计算各通道的均值（滤波核范围内像素点个数为kernel_size * kernel_size）
                mean_red = sum_red // (kernel_size * kernel_size)
                mean_green = sum_green // (kernel_size * kernel_size)
                mean_blue = sum_blue // (kernel_size * kernel_size)
                # 将均值设置到新图像对应的坐标位置
                new_image.putpixel((x, y), (mean_red, mean_green, mean_blue))
        #将新图像写回原图像
        self.image = new_image
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo        

    #组合空域滤波（先中值滤波再均值滤波）
    def btn3_3_click(self):
        #默认滤波核大小均为3
        kernel_size = 3
        # 获取图像的宽度和高度
        width, height = self.image.size
        # 计算中值滤波核半径（用于后续遍历像素点周边范围）
        radius = kernel_size // 2


        #运用均值滤波
        
        new_image = Image.new(self.image.mode, (width, height))
        # 遍历图像的每个像素点（避开边缘部分）
        for y in range(radius, height - radius):
            for x in range(radius, width - radius):
                # 分别用于存储红、绿、蓝通道滤波核覆盖像素点的像素值总和
                sum_red = 0
                sum_green = 0
                sum_blue = 0
                # 遍历滤波核范围内的像素点
                for ky in range(y - radius, radius + y):
                    for kx in range(x - radius, radius + x):
                        # 获取原图像中对应坐标的像素
                        pixel = self.image.getpixel((kx, ky))
                        red, green, blue = pixel
                        sum_red += red
                        sum_green += green
                        sum_blue += blue
                # 计算各通道的均值（滤波核范围内像素点个数为kernel_size * kernel_size）
                mean_red = sum_red // (kernel_size * kernel_size)
                mean_green = sum_green // (kernel_size * kernel_size)
                mean_blue = sum_blue // (kernel_size * kernel_size)
                # 将均值设置到新图像对应的坐标位置
                new_image.putpixel((x, y), (mean_red, mean_green, mean_blue))

        #运用中值滤波

        # 创建新的空白图像，尺寸与原图像相同，模式也相同
        result_image = Image.new(self.image.mode, (width, height))
        # 遍历图像的每个像素点（避开边缘部分）
        for y in range(radius, height - radius):
            for x in range(radius, width - radius):
                # 分别用于存储红、绿、蓝通道滤波核覆盖像素点的像素值列表
                red_pixel_values = []
                green_pixel_values = []
                blue_pixel_values = []
                # 遍历滤波核范围内的像素点
                for ky in range(y - radius, radius + y):
                    for kx in range(x - radius, radius + x):
                        # 获取原图像中对应坐标的像素
                        pixel = new_image.getpixel((kx, ky))
                        red, green, blue = pixel
                        red_pixel_values.append(red)
                        green_pixel_values.append(green)
                        blue_pixel_values.append(blue)
                # 对各通道的像素值列表进行排序
                sorted_red_pixel_values = sorted(red_pixel_values)
                sorted_green_pixel_values = sorted(green_pixel_values)
                sorted_blue_pixel_values = sorted(blue_pixel_values)
                # 取各通道的中值
                median_red = sorted_red_pixel_values[len(sorted_red_pixel_values) // 2]
                median_green = sorted_green_pixel_values[len(sorted_green_pixel_values) // 2]
                median_blue = sorted_blue_pixel_values[len(sorted_blue_pixel_values) // 2]
                # 将中值设置到新图像对应的坐标位置
                result_image.putpixel((x, y), (median_red, median_green, median_blue))


        self.image = result_image
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo    

    #频域滤波（默认使用理想低通滤波，默认截止频率为120）
    def btn3_4_click(self):
        #默认截止频率为120
        cutoff_frequency = 120
        image_array = np.array(self.image)

        #  对每个颜色通道分别进行处理（RGB图像有3个通道）
        filtered_image_array = np.zeros_like(image_array)
        for channel in range(3):
            # 获取当前通道的二维数组
            channel_data = image_array[:, :, channel]
            # 进行二维离散傅里叶变换（DFT），将空域转换到频域
            dft = np.fft.fft2(channel_data)
            # 将零频率分量移到频谱中心，便于后续处理和可视化
            dft_shifted = np.fft.fftshift(dft)
            # 获取频谱的行数和列数
            rows, cols = dft_shifted.shape
            # 计算频域中每个点到频谱中心的距离（用于判断是否在通带内）
            crow, ccol = rows // 2, cols // 2
            distance_matrix = np.sqrt((np.arange(rows)[:, np.newaxis] - crow) ** 2 + (np.arange(cols) - ccol) ** 2)
            # 根据截止频率构建理想低通滤波器的掩模（在通带内为1，通带外为0）
            mask = np.where(distance_matrix <= cutoff_frequency, 1, 0)
            # 将滤波器掩模应用到频域数据上（频域相乘相当于空域卷积）
            filtered_dft_shifted = dft_shifted * mask
            # 将零频率分量移回原来的位置
            filtered_dft = np.fft.ifftshift(filtered_dft_shifted)
            # 进行二维离散傅里叶逆变换（IDFT），将频域转换回空域
            filtered_channel_data = np.fft.ifft2(filtered_dft)
            # 取实部并转换为合适的数据类型（uint8）
            filtered_channel_data = np.real(filtered_channel_data).astype(np.uint8)
            # 将滤波后的通道数据放回原图像数组对应的位置
            filtered_image_array[:, :, channel] = filtered_channel_data

        #将处理后的数组转为图像后并赋回给原图像 
        self.image = Image.fromarray(filtered_image_array)
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo          






    ############################################################################################
    # 第四任务方法回退到上一级目录(销毁按钮)
    def btn4_back(self) : 
        self.btn_back.destroy()
        self.btn4_1.destroy()
        self.btn4_2.destroy()
        self.btn_1_create()

    # 第四任务方法 -- 图像锐化
    def btn4_click(self) :
        self.btn_1_delete()
        self.btn_back = Button(self , text="<- 回退到上一级" , width=20 , height=2 , command=self.btn4_back)
        self.canvas.create_window(850 , 40 , window=self.btn_back)

        self.btn4_1 = Button(self , text = "线性锐化滤波" , width=20 , height=4 , command=self.btn4_1_click)
        self.canvas.create_window(850 , 150 , window=self.btn4_1)
        self.btn4_2 = Button(self , text = "非线性锐化滤波" , width=20 , height=4 , command=self.btn4_2_click)
        self.canvas.create_window(850 , 300 , window=self.btn4_2)
        return
    
    #线性锐化滤波(默认滤波核大小为3)
    def btn4_1_click(self):
        #默认滤波核大小为3
        kernel_size = 3
        # 读取图像的尺寸
        width, height = self.image.size
        # 创建新的空白图像，尺寸与原图像相同，模式也相同，用于存储锐化后的图像
        new_image = Image.new(self.image.mode, (width, height))
        # 定义简单的拉普拉斯算子（3x3形式）
        laplacian_kernel = [[0, -1, 0],
                            [-1, 4, -1],
                            [0, -1, 0]]
        # 遍历图像的每个像素点（避开边缘部分）
        for y in range(1, height - 1):
            for x in range(1, width - 1):
                # 分别用于累计红、绿、蓝通道经过拉普拉斯算子处理后的像素值总和
                sum_red = 0
                sum_green = 0
                sum_blue = 0
                # 遍历拉普拉斯算子范围内的像素点
                for ky in range(3):
                    for kx in range(3):
                        pixel = self.image.getpixel((x + kx - 1, y + ky - 1))
                        red, green, blue = pixel
                        sum_red += red * laplacian_kernel[ky][kx]
                        sum_green += green * laplacian_kernel[ky][kx]
                        sum_blue += blue * laplacian_kernel[ky][kx]
                # 获取原图像对应坐标的像素值
                original_pixel = self.image.getpixel((x, y))
                red_original, green_original, blue_original = original_pixel
                # 计算锐化后的像素值
                alpha = 1.5  # 锐化强度系数默认为1.5
                red_sharpened = int(red_original + alpha * sum_red)
                green_sharpened = int(green_original + alpha * sum_green)
                blue_sharpened = int(blue_original + alpha * sum_blue)
                # 限制像素值在0-255范围内
                red_sharpened = max(0, min(255, red_sharpened))
                green_sharpened = max(0, min(255, green_sharpened))
                blue_sharpened = max(0, min(255, blue_sharpened))
                # 将锐化后的像素值设置到新图像对应坐标位置
                new_image.putpixel((x, y), (red_sharpened, green_sharpened, blue_sharpened))
        # 将结果赋回给原图像并显示
        self.image = new_image
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo        

    #非线性锐化滤波 (这里用局部区域的方差来动态调整锐化程度)
    def btn4_2_click(self):
        # 读取图像尺寸
        width, height = self.image.size
        # 创建新的空白图像，尺寸与原图像相同，模式也相同，用于存储锐化后的图像
        new_image = Image.new(self.image.mode, (width, height))
        # 定义局部区域大小（这里以3x3为例，可根据实际调整）
        neighborhood_size = 3
        radius = neighborhood_size // 2
        # 遍历图像像素点进行非线性锐化处理（避开边缘部分，可后续优化边缘处理）
        for y in range(radius, height - radius):
            for x in range(radius, width - radius):
                # 用于累计各通道的局部像素值总和
                sum_red = 0
                sum_green = 0
                sum_blue = 0
                # 用于累计各通道的局部像素值平方总和
                sum_square_red = 0
                sum_square_green = 0
                sum_square_blue = 0
                # 遍历局部区域内的像素点
                for ky in range(-radius, radius + 1):
                    for kx in range(-radius, radius + 1):
                        pixel = self.image.getpixel((x + kx, y + ky))
                        red, green, blue = pixel
                        sum_red += red
                        sum_green += green
                        sum_blue += blue
                        sum_square_red += red ** 2
                        sum_square_green += green ** 2
                        sum_square_blue += blue ** 2
                # 计算局部区域内各通道的像素均值
                num_pixels = neighborhood_size ** 2
                mean_red = sum_red // num_pixels
                mean_green = sum_green // num_pixels
                mean_blue = sum_blue // num_pixels
                # 计算局部区域内各通道的方差
                variance_red = (sum_square_red // num_pixels) - (mean_red ** 2)
                variance_green = (sum_square_green // num_pixels) - (mean_green ** 2)
                variance_blue = (sum_square_blue // num_pixels) - (mean_blue ** 2)
                # 获取原图像当前像素点的像素值
                original_pixel = self.image.getpixel((x, y))
                red_original, green_original, blue_original = original_pixel
                # 根据方差计算锐化系数（经实验将方差除以2000再加一效果较好，加一是因为锐化系数要大于1）
                k_red = 1 + variance_red / 2000
                k_green = 1 + variance_green / 2000
                k_blue = 1 + variance_blue / 2000
                # 计算锐化后的像素值（限制在0-255范围内）
                red_sharpened = max(0, min(255, int(red_original * k_red)))
                green_sharpened = max(0, min(255, int(green_original * k_green)))
                blue_sharpened = max(0, min(255, int(blue_original * k_blue)))
                # 将锐化后的像素值设置到新图像对应坐标位置
                new_image.putpixel((x, y), (red_sharpened, green_sharpened, blue_sharpened))
        # 将结果赋回给原图像并显示
        self.image = new_image
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo        


    # 第五任务方法回退到上一级目录(销毁按钮)
    def btn5_back(self) : 
        self.btn_back.destroy()
        self.btn5_1.destroy()
        self.btn5_2.destroy()
        self.btn5_3.destroy()
        self.btn5_4.destroy()
        self.btn_1_create()

    # 第五任务方法 -- 边缘检测
    def btn5_click(self) : 
        self.btn_1_delete()
        self.btn_back = Button(self , text="<- 回退到上一级" , width=20 , height=2 , command=self.btn5_back)
        self.canvas.create_window(850 , 40 , window=self.btn_back)

        self.btn5_1 = Button(self , text = "Sobel算子边缘检测" , width=20 , height=2 , command=self.btn5_1_click)
        self.canvas.create_window(850 , 100 , window=self.btn5_1)
        self.btn5_2 = Button(self , text = "Prewwit算子边缘检测" , width=20 , height=2 , command=self.btn5_2_click)
        self.canvas.create_window(850 , 200 , window=self.btn5_2)
        self.btn5_3 = Button(self , text = "Laplacian算子边缘检测" , width=20 , height=2 , command=self.btn5_3_click)
        self.canvas.create_window(850 , 300 , window=self.btn5_3)
        self.btn5_4 = Button(self , text = "Canny算子边缘检测" , width=20 , height=2 , command=self.btn5_4_click)
        self.canvas.create_window(850 , 400 , window=self.btn5_4)
        return

    #sobel算子边缘检测
    def btn5_1_click(self):
        # 将原图像转换成灰度级图像
        l_image = self.image.convert('L')
        # 获取图像的宽度和高度
        width, height = l_image.size
        # 创建新的空白图像，尺寸与原图像相同，模式也相同，用于存储边缘检测结果
        new_image = Image.new('L', (width, height))
        # 定义Sobel算子（水平方向和垂直方向）
        sobel_x = [[-1, 0, 1],
                [-2, 0, 2],
                [-1, 0, 1]]
        sobel_y = [[-1, -2, -1],
                [0, 0, 0],
                [1, 2, 1]]
        # 遍历图像的每个像素点（避开边缘部分）
        for y in range(1, height - 1):
            for x in range(1, width - 1):
                # 计算水平方向梯度
                gradient_x = 0
                for ky in range(3):
                    for kx in range(3):
                        pixel = l_image.getpixel((x + kx - 1, y + ky - 1))
                        gradient_x += pixel * sobel_x[ky][kx]
                # 计算垂直方向梯度
                gradient_y = 0
                for ky in range(3):
                    for kx in range(3):
                        pixel = l_image.getpixel((x + kx - 1, y + ky - 1))
                        gradient_y += pixel * sobel_y[ky][kx]
                # 计算梯度幅值（采用平方根法）
                gradient_magnitude = math.sqrt(gradient_x ** 2 + gradient_y ** 2)
                # 将梯度幅值归一化到0-255范围（方便显示和保存为图像）
                normalized_gradient = int(gradient_magnitude / (math.sqrt(2) * 255) * 255)
                # 将归一化后的梯度幅值设置到新图像对应的坐标位置
                new_image.putpixel((x, y), normalized_gradient)
        # 将结果赋回给原图像并显示
        self.image = new_image
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo   

    #prewwit算子边缘检测
    def btn5_2_click(self):
        #将原图像转为灰度图像
        l_image = self.image.convert('L')
        # 获取图像的宽度和高度
        width, height = l_image.size
        # 创建新的空白图像，尺寸与原图像相同，模式也相同，用于存储边缘检测结果
        new_image = Image.new('L', (width, height))
        # 定义Prewitt算子（水平方向和垂直方向）
        prewitt_x = [[-1, 0, 1],
                    [-1, 0, 1],
                    [-1, 0, 1]]
        prewitt_y = [[-1, -1, -1],
                    [0, 0, 0],
                    [1, 1, 1]]
        # 遍历图像的每个像素点（避开边缘部分，边缘可后续单独处理或者忽略）
        for y in range(1, height - 1):
            for x in range(1, width - 1):
                # 计算水平方向梯度
                gradient_x = 0
                for ky in range(3):
                    for kx in range(3):
                        pixel = l_image.getpixel((x + kx - 1, y + ky - 1))
                        gradient_x += pixel * prewitt_x[ky][kx]
                # 计算垂直方向梯度
                gradient_y = 0
                for ky in range(3):
                    for kx in range(3):
                        pixel = l_image.getpixel((x + kx - 1, y + ky - 1))
                        gradient_y += pixel * prewitt_y[ky][kx]
                # 计算梯度幅值（采用平方根法）
                gradient_magnitude = math.sqrt(gradient_x ** 2 + gradient_y ** 2)
                # 将梯度幅值归一化到0-255范围（方便显示和保存为图像）
                normalized_gradient = int(gradient_magnitude / (math.sqrt(2) * 255) * 255)
                # 将归一化后的梯度幅值设置到新图像对应的坐标位置
                new_image.putpixel((x, y), normalized_gradient)
        # 将结果赋回给原图像并显示
        self.image = new_image
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo   

    #laplacian算子边缘检测
    def btn5_3_click(self):
        # 将原图像转为灰度图像
        l_image = self.image.convert('L')
        # 获取图像的宽度和高度
        width, height = l_image.size
        # 创建新的空白图像，尺寸与原图像相同，模式也相同，用于存储边缘检测结果
        new_image = Image.new('L', (width, height))
        # 定义拉普拉斯算子（常用的一种简单形式）
        laplacian_kernel = [[0, 1, 0],
                            [1, -4, 1],
                            [0, 1, 0]]
        # 遍历图像的每个像素点（避开边缘部分，边缘可后续单独处理或者忽略）
        for y in range(1, height - 1):
            for x in range(1, width - 1):
                # 用于累计经过拉普拉斯算子处理后的像素值
                sum_value = 0
                # 遍历拉普拉斯算子范围内的像素点
                for ky in range(3):
                    for kx in range(3):
                        pixel = l_image.getpixel((x + kx - 1, y + ky - 1))
                        sum_value += pixel * laplacian_kernel[ky][kx]
                # 对处理后的像素值进行处理,乘以5是为了让边缘显示更加直观
                edge_value = abs(sum_value) * 5
                # 将边缘检测值归一化到0-255范围（方便显示和保存为图像）
                normalized_value = int(edge_value / (4 * 255) * 255)
                # 将归一化后的边缘检测值设置到新图像对应的坐标位置
                new_image.putpixel((x, y), normalized_value)
        # 将结果赋回给原图像并显示
        self.image = new_image
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo       

    # canny算子边缘检测
    def btn5_4_click(self):
        # 将原图像转为灰度图像
        l_image = self.image.convert('L')
        # 获取图像的宽度和高度
        width, height = l_image.size
        # 创建新的空白图像，尺寸与原图像相同，模式也相同，用于存储边缘检测结果
        new_image = Image.new('L', (width, height))

        # 1. 先用高斯滤波对图像进行消噪
        def gaussian_filter(image):
            """
            对图像进行简单的高斯滤波。
            """
            kernel_size = 3
            radius = kernel_size // 2
            gaussian_kernel = [[1 / 16, 2 / 16, 1 / 16],
                            [2 / 16, 4 / 16, 2 / 16],
                            [1 / 16, 2 / 16, 1 / 16]]
            filtered_image = Image.new('L', (width, height))
            for y in range(radius, height - radius):
                for x in range(radius, width - radius):
                    sum_value = 0
                    for ky in range(-radius, radius + 1):
                        for kx in range(-radius, radius + 1):
                            pixel = image.getpixel((x + kx, y + ky))
                            sum_value += pixel * gaussian_kernel[ky + radius][kx + radius]
                    filtered_image.putpixel((x, y), int(sum_value))
            return filtered_image

        filtered_image = gaussian_filter(l_image)

        # 2. 计算梯度幅值和方向
        gradient_x = [[0] * width for _ in range(height)]
        gradient_y = [[0] * width for _ in range(height)]
        gradient_magnitude = [[0] * width for _ in range(height)]
        gradient_direction = [[0] * width for _ in range(height)]
        sobel_x = [[-1, 0, 1],
                [-2, 0, 2],
                [-1, 0, 1]]
        sobel_y = [[-1, -2, -1],
                [0, 0, 0],
                [1, 2, 1]]
        for y in range(1, height - 1):
            for x in range(1, width - 1):
                # 计算水平方向梯度
                sum_x = 0
                for ky in range(3):
                    for kx in range(3):
                        pixel = filtered_image.getpixel((x + kx - 1, y + ky - 1))
                        sum_x += pixel * sobel_x[ky][kx]
                gradient_x[y][x] = sum_x
                # 计算垂直方向梯度
                sum_y = 0
                for ky in range(3):
                    for kx in range(3):
                        pixel = filtered_image.getpixel((x + kx - 1, y + ky - 1))
                        sum_y += pixel * sobel_y[ky][kx]
                gradient_y[y][x] = sum_y
                # 计算梯度幅值（采用平方根法）
                gradient_magnitude[y][x] = math.sqrt(sum_x ** 2 + sum_y ** 2)
                # 计算梯度方向（弧度制，转换到0-2*pi范围）
                gradient_direction[y][x] = math.atan2(sum_y, sum_x)
                if gradient_direction[y][x] < 0:
                    gradient_direction[y][x] += 2 * math.pi

        # 3. 非极大值抑制
        def non_maximum_suppression(gradient_magnitude, gradient_direction):
            """
            对梯度幅值进行非极大值抑制。
            """
            suppressed_image = [[0] * width for _ in range(height)]
            for y in range(1, height - 1):
                for x in range(1, width - 1):
                    angle = gradient_direction[y][x]
                    if (0 <= angle < math.pi / 8) or (7 * math.pi / 8 <= angle <= 2 * math.pi):
                        neighbor_1 = gradient_magnitude[y][x - 1]
                        neighbor_2 = gradient_magnitude[y][x + 1]
                    elif (math.pi / 8 <= angle < 3 * math.pi / 8):
                        neighbor_1 = gradient_magnitude[y - 1][x + 1]
                        neighbor_2 = gradient_magnitude[y + 1][x - 1]
                    elif (3 * math.pi / 8 <= angle < 5 * math.pi / 8):
                        neighbor_1 = gradient_magnitude[y - 1][x]
                        neighbor_2 = gradient_magnitude[y + 1][x]
                    else:
                        neighbor_1 = gradient_magnitude[y - 1][x - 1]
                        neighbor_2 = gradient_magnitude[y + 1][x + 1]
                    if gradient_magnitude[y][x] >= neighbor_1 and gradient_magnitude[y][x] >= neighbor_2:
                        suppressed_image[y][x] = gradient_magnitude[y][x]
            return suppressed_image

        suppressed_image = non_maximum_suppression(gradient_magnitude, gradient_direction)

        # 4. 双阈值检测与边缘连接
        high_threshold = 100  # 高阈值，可根据图像情况调整
        low_threshold = 50  # 低阈值，可根据图像情况调整
        final_image = [[0] * width for _ in range(height)]
        for y in range(1, height - 1):
            for x in range(1, width - 1):
                if suppressed_image[y][x] >= high_threshold:
                    final_image[y][x] = 255
                elif suppressed_image[y][x] >= low_threshold:
                    # 检查8邻域内是否有强边缘（值为255的像素）
                    has_strong_neighbor = False
                    for ky in range(-1, 2):
                        for kx in range(-1, 2):
                            if suppressed_image[y + ky][x + kx] >= high_threshold:
                                has_strong_neighbor = True
                                break
                        if has_strong_neighbor:
                            break
                    if has_strong_neighbor:
                        final_image[y][x] = 255
        for y in range(height):
            for x in range(width):
                new_image.putpixel((x, y), final_image[y][x])
        # 将结果赋回给原图像并显示
        self.image = new_image
        photo = ImageTk.PhotoImage(self.image)
        self.canvasRightChild.create_image(0, 0, anchor=NW, image=photo)
        self.canvasRightChild.image = photo      


   

    # 第六任务方法回退到上一级目录(销毁按钮)
    def btn6_back(self) : 
        self.btn_back.destroy()
        self.btn6_1.destroy()
        self.btn6_2.destroy()
        self.btn6_3.destroy()
        self.btn_1_create()

    # 第六任务方法 -- 图像分割
    def btn6_click(self) :
        self.btn_1_delete()
        self.btn_back = Button(self , text="<- 回退到上一级" , width=20 , height=2 , command=self.btn6_back)
        self.canvas.create_window(850 , 40 , window=self.btn_back)

        self.btn6_1 = Button(self , text = "阈值法" , width=20 , height=4)
        self.canvas.create_window(850 , 120 , window=self.btn6_1)
        self.btn6_2 = Button(self , text = "区域生长法" , width=20 , height=4)
        self.canvas.create_window(850 , 240 , window=self.btn6_2)
        self.btn6_3 = Button(self , text = "区域分裂与合并" , width=20 , height=4)
        self.canvas.create_window(850 , 360 , window=self.btn6_3) 
        return
    
    # 第七任务方法回退到上一级目录(销毁按钮)
    def btn7_back(self) : 
        self.btn_back.destroy()
        self.btn7_1.destroy()
        self.btn7_2.destroy()
        self.btn_1_create()

    # 第七任务方法 -- 图像分类
    def btn7_click(self) : 
        self.btn_1_delete()
        self.btn_back = Button(self , text="<- 回退到上一级" , width=20 , height=2 , command=self.btn7_back)
        self.canvas.create_window(850 , 40 , window=self.btn_back)

        self.btn7_1 = Button(self , text = "自建卷积网络" , width=20 , height=4)
        self.canvas.create_window(850 , 120 , window=self.btn7_1)
        self.btn7_2 = Button(self , text = "经典神经网络" , width=20 , height=4)
        self.canvas.create_window(850 , 240 , window=self.btn7_2)
        return

    ##############################################
    # 用来绘制窗体中的内容
    def createWidgets(self) : 
        self.canvas = Canvas(self , width=1700 , height=800)
        self.canvas.pack()

        # 上传按钮
        self.btnUpload = Button(self , text = "上传图片" , width=10 , height=2 , command=self.uploadPicture)
        self.canvas.create_window(150 , 50 , window=self.btnUpload)
        # 下载按钮
        self.btnDownload = Button(self , text = "下载图片" , width=10 , height=2  , command=self.downloadPicture)
        self.canvas.create_window(1050 , 50 , window=self.btnDownload)
        

        # 构建一个输入框用来输入图片的本地地址
        self.entry = Entry(self , fg="gray")
        self.entry.insert(0 , "请输入图片的完整路径~")

        # 后面要点击时自动清除文本
        # self.entry.bind("<FocusIn>" , self.on_entry_click)
        # self.entry.bind("<FocusOut>" , self.on_focusout)
        
        self.canvas.create_window(300 , 50 , window=self.entry)

        self.btn_1_create()

        self.canvasChild = Canvas(self , width = 600 , height=600 , bg="gray")
        self.canvas.create_window(400 , 400 , window=self.canvasChild)
        
        self.canvasRightChild = Canvas(self , width = 600 , height=600 , bg="gray")
        self.canvas.create_window(1300 , 400 , window=self.canvasRightChild)


app = Application()
app.master.title('Final Project of Computer Image')
app.mainloop()
